#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/tcp.h>
#include <netinet/ip6.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <time.h>
#include <stdint.h>
#include <unistd.h>
#include "flow_tag.h"
#define DESTINATION_IP "::1"
char random_src_ip[40];
char random_dst_ip[40];
extern void random_ipv6(char *src_ip, char *dst_ip);
extern unsigned short get_ipv6_tcp_checksum(unsigned short *addr, int len, struct in6_addr src_ip, struct in6_addr dst_ip);
extern uint64_t random_num(uint64_t start, uint64_t end);
int main(int argc,char **argv) {
    srand(time(NULL));
    int sockfd;
    char buffer[4096]={0};
    memset(buffer,0,sizeof(buffer));
    struct ip6_hdr *iph = (struct ip6_hdr *) buffer;
    struct tcphdr *tcph = (struct tcphdr *) (buffer + sizeof(struct ip6_hdr));
    struct flow_tag *tag = (struct flow_tag *) (buffer + sizeof(struct ip6_hdr) + sizeof(struct tcphdr));
    sockfd = socket(AF_INET6, SOCK_RAW, IPPROTO_RAW);
    if (sockfd < 0) {
        perror("socket");
        exit(1);
    }
    memset(buffer, 0, sizeof(buffer));
    iph->ip6_flow = htonl((6 << 28) | (0 << 20) | 0);
    iph->ip6_plen = htons(sizeof(struct tcphdr)+sizeof(struct flow_tag));
    iph->ip6_nxt = IPPROTO_TCP; 
    iph->ip6_hops = random_num(10,20); 
    tcph->source = htons(random_num(1000,2000));
    tcph->dest = htons(random_num(10000,20000));
    tcph->seq = htonl(random_num(10000,20000));
    tcph->ack_seq = htonl(random_num(10000,20000));
    tcph->doff = 5; 
    tcph->syn = 1;
    tcph->fin = 1;
    tcph->psh = 1;
    tcph->ack = 1;
    tcph->rst = 1;
    tcph->urg = 1;
    tcph->window = htons(random_num(500,1000));
    tcph->urg_ptr = 0;
    if(argc < 3){
        random_ipv6(random_src_ip,random_dst_ip);
        const char* source_ip = random_src_ip;
        printf("source ip is %s\n",source_ip);
        const char* dst_ip = random_dst_ip;
        printf("destination ip is %s\n",dst_ip);
        inet_pton(AF_INET6, source_ip, &(iph->ip6_src));
        inet_pton(AF_INET6, dst_ip, &(iph->ip6_dst));
        inet_pton(AF_INET6, source_ip, &(tag->src_ip));
        inet_pton(AF_INET6, dst_ip, &(tag->dst_ip));
    }
    else{
        const char* source_ip = argv[1];
        printf("source ip is %s\n",source_ip);
        const char* dst_ip = argv[2];
        printf("destination ip is %s\n",dst_ip);
        inet_pton(AF_INET6, source_ip, &(iph->ip6_src));
        inet_pton(AF_INET6, dst_ip, &(iph->ip6_dst));
        inet_pton(AF_INET6, source_ip, &(tag->src_ip));
        inet_pton(AF_INET6, dst_ip, &(tag->dst_ip));
    }
    tag->tag_id[1] = htonl(random_num(1,100000));
    for(int i = 0;i <=7;i++){
        tag->id_hash[i] = random_num(0,0xffffffff);
    	tag->tag_hash[i] = random_num(0,0xffffffff);
    }
    tag->data_id[1] = htonl(random_num(1,100000));
    tag->sent_num[1] = htonl(random_num(1,100000));
    uint64_t protocol_type = 1;
    uint64_t data_mode = random_num(1,15);
    uint64_t level = random_num(0,3)&2;//00 11
    uint64_t dst_id = random_num(0x2000,0x2fff);
    uint64_t src_id = random_num(0x1000,0x1fff);
    uint64_t data = (src_id<<36) + (dst_id<<8) + (level<<6) + (data_mode<<1) + protocol_type;
    tag->sent_num[2] = htonl((data>>32)&0xffffffff);
    tag->sent_num[3] = htonl(data&0xffffffff);
    tcph->check = get_ipv6_tcp_checksum((unsigned short *)tcph,sizeof(struct tcphdr) + sizeof(struct flow_tag),iph->ip6_src,iph->ip6_dst);
    struct sockaddr_in6 dest_addr;
    memset(&dest_addr, 0, sizeof(dest_addr));
    dest_addr.sin6_family = AF_INET6;
    inet_pton(AF_INET6, DESTINATION_IP, &dest_addr.sin6_addr);
    if (sendto(sockfd, buffer, sizeof(struct ip6_hdr)+sizeof(struct tcphdr)+sizeof(struct flow_tag), 0, (struct sockaddr *) &dest_addr, sizeof(dest_addr)) < 0) {
        perror("sendto");
        exit(1);
    }
    close(sockfd);
    return 0;
}